; 使用四位数码管显示器，利用扫描方式循环显示0000 ~ 9999两位数
; 本程序可以显示的最大值是65535

ORG 0000H

; SJMP 短跳转，范围从-128到+127字节
; LJMP 长跳转，可以跳转到存储器中的任何地址
LJMP Main
ORG 0030H

Main:
MOV DPTR, #7921  ; 改变该四位数可以改变显示值
; 先放高位，后放低位
MOV R1, DPH  ; 27
MOV R0, DPL  ; 0F
CALL HexToBcd
LCALL Display
RET

HexToBcd:
; 共5位
; R4低四位 万位
; R3高四位 千位
; R3低四位 百位
; R2高四位 十位
; R2低四位 个位
MOV R2, #00H
MOV R3, #00H
MOV R4, #00H
MOV R5, #10H  ; #16，记数循环16次

; 使用double dabble算法（移位加三法）将二进制转换为BCD
Loop:
CLR C  ; 清除进位cy（PSW寄存器中的第7位）
MOV A, R0  ; 将待显示的四位数低2位放入累加器A
RLC A  ; 带进位左移 左移A寄存器并加上之前的进位
MOV R0, A
MOV A, R1
RLC A
MOV R1, A

MOV A, R2
; 自身相加，并加上cy位，在cy第一次出现1之前R2一直为0
; 相当于每移1位，R2中的值翻一倍
ADDC A, R2  ; 带进位标志的加法 A + R2 + cy
; DA 十进制调整指令，十位超出时会产生进位，自动完成算法中的移位加三（+ 6）
; 若A的低4位大于9或auxiliary carry（ac）被设置，那么给低4位加6
; 若A的高4位大于9或carry（cy）被设置，那么给高4位加6
DA A
MOV R2, A

MOV A, R3
ADDC A, R3
DA A
MOV R3, A

MOV A, R4
ADDC A, R4
; R4万位不会超过9，不需要DA
MOV R4, A

DJNZ R5, Loop
RET

Display:
MOV DPTR, #Table
;MOV R7, #50
; 不显示万位
; 千位
MOV A, #1CH
MOV P2, A
MOV A, R3
SWAP A  ; 交换累加器A（8位）中的高四位和低四位
ANL A, #0FH  ; 将A的值和0FH按位与，A的高四位清零，只保留低四位的值（R3的高四位）
MOVC A, @A + DPTR
MOV P0, A
ACALL Delay_10ms  ; 在当前2K字节页内调用子程序Delay_10ms
; 百位
MOV A, #18H
MOV P2, A
MOV A, R3
ANL A, #0FH
MOVC A, @A + DPTR
MOV P0, A
ACALL Delay_10ms
; 十位
MOV A, #14H
MOV P2, A
MOV A, R2
SWAP A
ANL A, #0FH
MOVC A, @A + DPTR
MOV P0, A
ACALL Delay_10ms
; 个位
MOV A, #10H
MOV P2, A
MOV A, R2
ANL A, #0FH
MOVC A, @A + DPTR
MOV P0, A
ACALL Delay_10ms

DJNZ R7, Display
;JMP Display

Delay_10ms:  ; (2 * 248 + 3) * 2 + 3 = 1001 μs
MOV R6, #2
Delay:
MOV R7, #248
DJNZ R7, $
DJNZ R6, Delay
RET

Table:
DB 3FH, 06H, 5BH, 4FH, 66H, 6DH, 7DH, 07H
DB 7FH, 6FH, 77H, 7CH, 39H, 5EH, 79H, 71H


END